﻿using System;
using System.Linq;
using Fujitsu.JiraAnalysis.JiraRPC;
using System.Collections.Generic;

namespace Fujitsu.JiraAnalysis.TrackingSystemAdapter
{
    public class TrackSystemInfoAdapter : ITrackSystemInfo
    {
        public Credentials Credential { get; set; }
        private JiraSoapServiceService JiraSoapService { get; set; }

        public TrackSystemInfoAdapter()
        { }

        public TrackSystemInfoAdapter(string login, string password, string serviceUrl)
        {
            Credential = new Credentials(login, password, serviceUrl);
            JiraSoapService = new JiraSoapServiceService { Url = Credential.ServiceUrl };
        }

        public void OpenConnection()
        {
            try
            {
                if (string.IsNullOrEmpty(Credential.Token))
                {
                    Credential.Token = JiraSoapService.login(Credential.Login, Credential.Password);
                }
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, "Couldn't login to service!", ex.Message);
            }
        }

        public List<RemoteStatus> GetStatuses()
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getStatuses(Credential.Token).ToList();
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, "Can't get issues' statuses.", ex.Message);
            }
        }

        public List<RemotePriority> GetPriorities()
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getPriorities(Credential.Token).ToList();
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, "Can't get issues' priorities.", ex.Message);
            }
        }

        public List<RemoteResolution> GetResolutions()
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getResolutions(Credential.Token).ToList();
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, "Can't get issues' resolutions.", ex.Message);
            }
        }

        public List<RemoteProject> GetProjects()
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getProjectsNoSchemes(Credential.Token).ToList();
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, "Can't get projects.", ex.Message);
            }
        }

        public RemoteProject GetProjectByKey(string projectKey)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getProjectByKey(Credential.Token, projectKey);
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get project '{0}'", projectKey), ex.Message);
            }
        }

        private List<RemoteIssueType> GetIssueTypesForProject(string projectId)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getIssueTypesForProject(Credential.Token, projectId).ToList();
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get issues' types for project '{0}'", projectId), ex.Message);
            }
        }

        public List<RemoteIssueType> GetIssueTypesForProjectKey(string projectKey)
        {
            OpenConnection();
            var jiraProjectId = GetProjectByKey(projectKey).id;
            return GetIssueTypesForProject(jiraProjectId);
        }

        public List<RemoteIssueType> GetSubtaskIssueTypesForProject(string projectId)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getSubTaskIssueTypesForProject(Credential.Token, projectId).ToList();
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get subtasks issues' types for project '{0}'", projectId), ex.Message);
            }

        }

        private IEnumerable<RemoteIssue> GetIssuesForProject(string projectKey)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getIssuesFromJqlSearch(Credential.Token, string.Format("project = '{0}'", projectKey), int.MaxValue);
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                if (ex.Message == "No such operation 'getIssuesFromJqlSearch'")
                    return JiraSoapService.getIssuesFromTextSearchWithProject(Credential.Token, new[] { projectKey }, "", int.MaxValue);
                
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get issues of project '{0}'", projectKey), ex.Message);
            }
        }

        public IEnumerable<RemoteIssue> GetIssuesForProject(string projectKey, bool isIncludeSubtasks)
        {
            try
            {
                OpenConnection();
                var jiraIssues = GetIssuesForProject(projectKey);

                if (isIncludeSubtasks)
                {
                    return jiraIssues;
                }

                var subtaskIssueTypes = JiraSoapService.getSubTaskIssueTypes(Credential.Token);
                return jiraIssues.Where(jiraIssue => !subtaskIssueTypes.Any(s => s.id == jiraIssue.type));
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get issues of project '{0}'", projectKey), ex.Message);
            }
        }

        public IEnumerable<RemoteWorklog> GetWorkLogs(string issueKey)
        {
            return GetWorkLogs(issueKey, true);
        }

        public IEnumerable<RemoteWorklog> GetWorkLogs(string issueKey, bool isToTrySecondTime)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getWorklogs(Credential.Token, issueKey);
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                if (isToTrySecondTime)
                    return GetWorkLogs(issueKey, false);
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get worklogs of issue '{0}'", issueKey), ex.Message);
            }
        }

        public RemoteUser GetUser(string username)
        {
            return GetUser(username, true);
        }

        public RemoteUser GetUser(string username, bool isToTrySecondTime)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getUser(Credential.Token, username);
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                if (isToTrySecondTime)
                    return GetUser(username, false);
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get User '{0}'", username), ex.Message);
            }
        }

        public IEnumerable<RemoteVersion> GetProjectVersions(string projectKey)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getVersions(Credential.Token, projectKey);
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get versions of '{0}' project", projectKey), ex.Message);
            }
        }

        public RemoteIssue GetIssue(string issueKey)
        {
            try
            {
                OpenConnection();
                return JiraSoapService.getIssue(Credential.Token, issueKey);
            }
            catch (TrackSystemCommunicationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TrackSystemCommunicationException(Credential, string.Format("Can't get issue '{0}'", issueKey), ex.Message);
            }
        }
    }

    public interface IAdapterCreator
    {
        ITrackSystemInfo CreateJiraSoapServiceAdapter(string login, string password, string serviceUrl);
    }
    public class AdapterCreator : IAdapterCreator
    {
        public ITrackSystemInfo CreateJiraSoapServiceAdapter(string login, string password, string serviceUrl)
        {
            return new TrackSystemInfoAdapter(login, password, serviceUrl);
        }
    }
}
